/**
 *
 */
package com.newer.config.shiro;

import com.newer.biz.sys.auth.ShiroBiz;
import com.newer.biz.sys.menu.MenuInfoBiz;
import com.newer.commons.constants.CommonConstants;
import com.newer.commons.result.BizResult;
import com.newer.config.shiro.bean.MyShiroFilterFactoryBean;
import com.newer.config.shiro.filters.AnyPermissionsAuthorizationFilter;
import com.newer.config.shiro.filters.AnyRoleAuthorizationFilter;
import com.newer.config.shiro.realms.ShiroAuthcRealm;
import com.newer.dao.bean.sys.Menu;
import com.newer.dao.bean.sys.MenuPermission;
import com.newer.utils.ShiroAuthUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static com.newer.utils.ShiroAuthUtils.menuAuth;

/**
 * Shiro 配置
 * <p>
 * Apache Shiro 核心通过 Filter 来实现，
 * 就好像SpringMvc 通过DispachServlet 来主控制一样。
 * 既然是使用 Filter 一般也就能猜到，是通过URL规则来进行过滤和权限校验，
 * 所以我们需要定义一系列关于URL的规则和访问权限。
 *
 * @author 寻添俊
 * 2018年3月12日-上午10:49:58
 */
@Configuration
public class ShiroConfiguration {


    /**
     * 配置密码比较器
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher credentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName(CommonConstants.ENCRYP_TYPE);
        matcher.setHashIterations(CommonConstants.ENCRYP_TIMES);
        matcher.setStoredCredentialsHexEncoded(true);
        return matcher;
    }

    /**
     * 配置缓存信息
     *
     * @return
     */
    @Bean
    public EhCacheManager getEhCacheManager() {
        EhCacheManager em = new EhCacheManager();
        em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        return em;
    }

    /**
     * 配置自定义域
     *
     * @param cacheManager
     * @return
     */
    @Bean(name = "shiroAuthcRealm")
    public ShiroAuthcRealm shiroAuthcRealm(HashedCredentialsMatcher credentialsMatcher,
                                           EhCacheManager cacheManager) {
        ShiroAuthcRealm realm = new ShiroAuthcRealm();
        realm.setCacheManager(cacheManager);
        realm.setCredentialsMatcher(credentialsMatcher);
        return realm;
    }

    /**
     * cookie对象;
     * rememberMeCookie()方法是设置Cookie的生成模版，比如cookie的name，cookie的有效时间等等。
     *
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    /**
     * cookie管理对象;
     * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
        return cookieRememberMeManager;
    }


    /**
     * 定义SecurityManager
     *
     * @return
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(ShiroAuthcRealm shiroAuthcRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroAuthcRealm);
        //用户授权/认证信息Cache, 采用EhCache 缓存
        securityManager.setCacheManager(getEhCacheManager());
        //注入记住我管理器
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }


    /**
     * 管理shiro bean生命周期
     *
     * @return
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启Shiro权限注解
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }


    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意：单独一个ShiroFilterFactoryBean配置是或报错的，以为在
     * 初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
     * <p>
     * Filter Chain定义说明
     * 1、一个URL可以配置多个Filter，使用逗号分隔
     * 2、当设置多个过滤器时，全部验证通过，才视为通过
     * 3、部分过滤器可指定参数，如perms，roles
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(MenuInfoBiz menuInfoBiz, ShiroBiz shiroBiz, DefaultWebSecurityManager securityManager) {
        // 使用自定义的factoryBean对象
        ShiroFilterFactoryBean shiroFilterFactoryBean = new MyShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 设置角色/权限管理拦截
        Map<String, Filter> filterMap = shiroFilterFactoryBean.getFilters();
        filterMap.put("anyPerms", new AnyPermissionsAuthorizationFilter());
        filterMap.put("anyRoles", new AnyRoleAuthorizationFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

        // 添加默认信息配置
        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout.do", "logout");
        // 防止登录成功之后下载favicon.ico
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/login.do", "anon");

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.ftl"页面
        shiroFilterFactoryBean.setLoginUrl("/login.htm");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index.htm");
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403.htm");

        // 设置菜单访问权限
        filterChainDefinitionMap = menuAuth(menuInfoBiz, shiroBiz, filterChainDefinitionMap);

        // <!-- 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**", "authc");
        // 设置信息过滤
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


}
